#![feature(proc_macro_hygiene, decl_macro)]

#[macro_use]
extern crate rocket;

mod domain;
mod repository;

use std::io::Cursor;

use rocket::http::ContentType;
use rocket::request::Form;
use rocket::response::{NamedFile, Response};
use rocket_contrib::json::Json;
use serde_json;
use serde::{Serialize};
use sysinfo::{System, SystemExt, ComponentExt};

use crate::domain::entities::Item;
use crate::repository::medicine::{InMemoryRepo, Medicine};

#[derive(FromForm, Debug)]
struct QuantityForm {
    pub quantity: u32,
}

#[derive(Debug, Clone, Serialize)]
pub struct LoadAvg {
    pub one: f64,
    pub five: f64,
    pub fifteen: f64,
}

#[derive(Debug, Clone, Serialize)]
pub struct SystemStatus {
    pub load_avg: LoadAvg,
    pub memory_used_mib: f64,
    pub memory_total_mib: f64,
    pub temperature: f32,
}

#[get("/")]
fn index() -> NamedFile {
    NamedFile::open("index.html").unwrap()
}

#[get("/static/<name>")]
fn assets(name: String) -> NamedFile {
    NamedFile::open(format!("static/{}", name)).unwrap()
}

#[get("/medicines")]
fn list() -> Response<'static> {
    let repo = InMemoryRepo::new();
    let items = repo.list();
    let content = serde_json::to_string(&items).unwrap();
    let mut response = Response::new();
    response.set_header(ContentType::JSON);
    response.set_sized_body(Cursor::new(content));
    response
}

#[get("/status")]
fn status() -> Response<'static> {
    let sys = System::new_all();
    let load_avg = sys.load_average();
    let mut max_temp = 0.0;
    for comp in sys.components() {
        if max_temp < comp.temperature() {
            max_temp = comp.temperature();
        }
    }
    let status = SystemStatus{
        load_avg: LoadAvg{
            one: load_avg.one,
            five: load_avg.five,
            fifteen: load_avg.fifteen,
        },
        temperature: max_temp,
        memory_used_mib: 0.0,
        memory_total_mib: 0.0,
    };
    let content = serde_json::to_string(&status).unwrap();
    let mut response = Response::new();
    response.set_header(ContentType::JSON);
    response.set_sized_body(Cursor::new(content));
    response
}

#[post("/medicine", format = "application/json", data = "<data>")]
fn add(data: Json<Item>) -> &'static str {
    println!("Got request {:?}", data);
    let mut repo = InMemoryRepo::new();
    repo.add(data.into_inner());
    "ok"
}

#[put("/medicines/<id>", data = "<data>")]
fn update_quantity(id: Option<u32>, data: Form<QuantityForm>) -> &'static str {
    println!("Got request {:?}, {:?}", id, data);

    let mut repo = InMemoryRepo::new();
    let req = data.into_inner();
    repo.update_quantity(id.unwrap(), req.quantity);
    "ok"
}

fn main() {
    rocket::ignite()
        .mount("/api/v1", routes![add, list, update_quantity])
        .mount("/", routes![index, status, assets])
        .launch();
}
